/*
 * ENUM Discoverer. ENUM Demonstration software, demonstrating the potential of ENUM.
 * 
 * Copyright (C) 2009 SIDN and ISOC.nl
 * 
 * This file is part of ENUM Discoverer.
 *
 * ENUM Discoverer is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * ENUM Discoverer is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with ENUM Discoverer.  If not, see <http://www.gnu.org/licenses/>.
 */

package nl.sidn.enumdiscoverer.contacts.dao;

import nl.sidn.enumdiscoverer.LogConfig;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.provider.Contacts;
import android.provider.Contacts.People;
import android.provider.Contacts.Phones;
import android.util.Log;

/**
 * Abstracts access to Android's contacts storage mechanism
 * @author	Grant Patterson (grant.patterson@catalyst.net.nz)
 */
public class ContactsDao {

	static final String TAG = LogConfig.getLogTag(ContactsDao.class);
	// whether DEBUG level logging is enabled (whether globally, or explicitly for this log tag)
	static final boolean DEBUG = LogConfig.isDebug(TAG);
	// whether VERBOSE level logging is enabled
	static final boolean VERBOSE = LogConfig.VERBOSE;
	
	public static final String PHONE_NUM_COLUMN = Phones.NUMBER;
	public static final String PERSON_ID_COLUMN = Phones.PERSON_ID;
	
	private static final long NO_CONTACT = -1;
	
	private Context mContext;
	
	public ContactsDao(Context context) {
		if (VERBOSE) Log.v(TAG, this + " created");
		
		mContext = context;
	}
	
	/**
	 * Get a DAO for accessing and updating a contact.
	 * @param number A phone number of the contact
	 * @return A DAO for the contact.
	 *         null if the contact doesn't exist and createIfNonExistent is false.
	 */
	public PersonDao getDaoForContact(String number) {
		
		long id = getPersonId(number);
		
		if (id == NO_CONTACT) {
			return null;
		}
		
		return PersonDao.createDaoForContact(mContext, id);
	}
	
	/**
	 * Get a DAO for accessing and updating a contact.
	 * @param personId The personId of a contact
	 * @param createIfNonExistent Whether to create a new contact no contact has this number.
	 * @return A DAO for the contact.
	 *         null if the contact doesn't exist and createIfNonExistent is false.
	 */
	public PersonDao getDaoForContact(long personId) {
		
		if (contactExists(personId)) {
			return PersonDao.createDaoForContact(mContext, personId);
		} else {
			return null;
		}
	}
	
	/**
	 * Queries Contacts for all phone numbers. The returned Cursor's rows each contain a phone number
	 * and the ID of the contact with that number (ContactsDao.PHONE_NUM_COLUMN and
	 * ContactsDao.PERSON_ID_COLUMN)
	 * @return A Cursor over all phone numbers in Contacts
	 */
	public Cursor getAllPhoneNumbers() {
		
		String[] projection = {Phones.NUMBER, Phones.PERSON_ID};
		
		ContentResolver resolver = mContext.getContentResolver();
		return resolver.query(Phones.CONTENT_URI, projection, null, null, null);
	}
	
	public boolean contactExists(long personId) {
		
		String[] projection = {People._ID};
		Uri contactUri = ContentUris.withAppendedId(People.CONTENT_URI, personId);
		
		Cursor c = mContext.getContentResolver().query(contactUri, projection, null, null, null);
		
		try {
			int count = c.getCount();
			
			// personId is the primary key of Contacts.People, so count must be 1 or 0
			return count == 1;
			
		} finally {
			c.close();
		}
	}
	
	public void close() {
		if (VERBOSE) Log.v(TAG, this + " closed");
		
		// clear reference to context so we don't cause a memory leak
		mContext = null;
	}
	
	private long getPersonId(String number) {
		
		String[] projection = {Phones.PERSON_ID};
		Uri contactUri = Uri.withAppendedPath(Contacts.Phones.CONTENT_FILTER_URL, Uri.encode(number));
		
		Cursor c = mContext.getContentResolver().query(contactUri, projection, null, null, null);
		
		try {
			int count = c.getCount();
			
			if (count >= 1) {
				if (count > 1) {
					Log.i(TAG, "Multiple contacts for " + number + ". Using the first one found");
				}
				c.moveToNext();
				return c.getLong(0);
				
			} else { // count < 1
				return NO_CONTACT;
			}
		} finally {
			c.close();
		}
	}
	
	/**
	 * Create a new contact in the Contacts DB. The contact will contain no
	 * data.
	 * @return A PersonDao for accessing the new contact
	 */
	public PersonDao createContact() {
		
		ContentResolver resolver = mContext.getContentResolver();
		
		// including person in 'My Contacts Group' means it will be visible in Contacts app by default
		Uri uri = Contacts.People.createPersonInMyContactsGroup(resolver, new ContentValues());
		if (VERBOSE) Log.v(TAG, "Inserted a new contact: " + uri);
		
		long id = ContentUris.parseId(uri);
		
		return PersonDao.createDaoForNewContact(mContext, id);
	}
}
